# routers/case_router.py
import json
import os
import re
import time
import uuid
from io import StringIO
from typing import List, Dict, Any

from fastapi import APIRouter, Query, Depends, HTTPException, Body, BackgroundTasks
from ruamel.yaml import YAML
from starlette.responses import StreamingResponse

from Logs.log_handle import get_log  # 日志处理模块
from case import run_case
from case.automatic_scripting.get_script import generate_script_file, cleanup_file, stream_process
from routers.router_function.import_in_curl import curl_to_requst
from schemas.code_script_schemas import CodePayload, create_code_script, CodeScriptCreateDTO, CodeScriptQueryDTO, \
    get_code_script_list
from schemas.testcase_schemas import (  # 数据库操作模块
    TestcaseCreate,
    create_testcase_in_db,
    select_testcase_in_db,
    del_testcases_id_and_ids,
    update_testcases_case_in_db, TestcaseResponseCases, TestcaseResponse,
)
from schemas.variables_schemas import create_localvariable_in_db, select_localvariable_in_db, \
    select_globalvariable_in_db, LocalVariableCreate, GlobalVariableCreate
from tools.assert_util import assert_result
from tools.db_model_tool import config_path
from tools.decorators import return_response  # 响应格式化装饰器
from tools.request_utils.base_request import BaseRequest
from tools.request_utils.cases_global_variables import GLOBAL_VARS
from tools.request_utils.pre_handle_utils import str_to_python
from tools.yaml_handle import ReadYaml

# ---------------------------- 初始化配置 ----------------------------
# 获取日志对象（日志文件名为当前脚本名）
log = get_log(os.path.split(__file__)[-1])

# 创建路由实例，统一前缀为/testcases
router = APIRouter(prefix="/testcases", tags=["测试用例"])


# ---------------------------- API端点定义 ----------------------------


@router.post("/run_warm_cache")
@return_response()
async def warm_the_cache(
        testcases: List[TestcaseResponseCases]
):
    """
    预热缓存
    :param testcases: 要执行的测试用例列表
    """
    key = str(uuid.uuid4())  # 生成唯一 key

    # 序列化测试用例数据
    serialized_testcases = json.dumps([testcase.model_dump() for testcase in testcases], ensure_ascii=False)

    create_localvariable_in_db(LocalVariableCreate(
        local_name="未知名称",
        local_key=key,
        local_value=serialized_testcases,
        remark=""
    ))
    # 返回 key 作为 JSON 格式
    return {"key": key}


@router.post("/run_testcases")
@return_response()
async def run_testcases_keys(case_keys: str):
    """
    执行指定测试用例
    :param case_keys: 测试用例的键列表（格式："xxxx,*****,bbbb"）
    :return: 用例执行结果
    """
    # case_keys = "51823703-5632-4ba1-8c5e-fcfd25f9c087"
    log.info(f"接收到运行用例请求，case_keys: {case_keys}")

    if not case_keys:
        raise HTTPException(status_code=400, detail="测试用例键不能为空")

    case_key_list = [key.strip() for key in case_keys.split(",") if key.strip()]  # 去除空格并去掉空 key
    valid_keys_list = []  # 存储有效 key
    null_keys_list = []  # 存储无效 key

    for key in case_key_list:
        sql_data = select_localvariable_in_db(1, 999, LocalVariableCreate(local_key=key))
        if sql_data.get('data'):
            valid_keys_list.append(key)
        else:
            null_keys_list.append(key)

    if not valid_keys_list:
        raise HTTPException(status_code=400, detail="所有测试用例键都无效")

    # 只执行有效的 case_keys
    results = run_case.main(",".join(valid_keys_list))

    return {"null_keys": null_keys_list, "results": results}


@router.get("/list_testcases")
@return_response()
async def get_all_testcases(
        page: int = Query(1, ge=1, description="页码（从1开始）"),
        size: int = Query(10, ge=1, description="每页数据量"),
        testcase: TestcaseCreate = Depends()
):
    """
    分页获取测试用例列表

    :param page: 当前页码，默认1
    :param size: 每页数据量，默认10
    :param testcase: 其他的筛选条件
    :return: 分页后的测试用例列表
    """
    result = select_testcase_in_db(page, size, testcase)
    log.info(f"查询结果返回，共 {len(result)} 条记录")
    return result


@router.post("/create_testcase")
@return_response()
async def create_testcase(testcase: TestcaseCreate):
    """
    创建新测试用例

    :param testcase: 通过请求体传递的测试用例数据
    :return: 创建成功的测试用例详情
    """
    log.info(f"接收到创建用例请求，数据: {testcase}")
    result = create_testcase_in_db(testcase)
    return result


@router.post("/delete_testcases")
@return_response()
async def delete_testcases(case_ids: List[int]):
    """
    批量删除测试用例

    :param case_ids: 需要删除的测试用例ID列表
    :return: 删除操作结果
    """
    log.info(f"接收到删除用例请求，case_ids: {case_ids}")

    # 参数校验
    if not case_ids:
        log.error("case_ids 不能为空")
        raise HTTPException(status_code=400, detail="case_ids 不能为空")

    result = del_testcases_id_and_ids(case_ids)
    log.info(f"删除操作完成，删除了 {len(case_ids)} 条测试用例")
    return result


@router.post("/update_testcases")
@return_response()
async def update_testcases(
        testcase: TestcaseResponse
):
    """
    更新指定测试用例

    :param case_id: 目标测试用例ID
    :param testcase: 更新后的数据
    :return: 更新后的测试用例详情
    """
    log.info(f"接收到更新用例请求，case_id: {testcase.case_id}, 更新数据: {testcase.dict()}")
    result = update_testcases_case_in_db(testcase)
    log.info(f"更新测试用例成功，case_id: {testcase.case_id}")
    return result


@router.post("/import_curl_case")
@return_response()
async def import_curl_testcases(curl: str = Body(..., embed=True)):
    """
    导入cURL命令生成测试用例

    处理流程：
    1. 解析cURL命令获取请求参数
    2. 将解析结果转换为测试用例模型
    3. 存储到数据库

    :param curl: 通过body提交的cURL命令（JSON格式）
    :return: 创建的测试用例详情
    """

    # 解析 cURL 命令
    url, pk, headers, method, data_dict, cookies = curl_to_requst(str(curl))

    # 读取配置文件中的 host
    yaml_data = ReadYaml(config_path).read_yaml

    # 构建测试用例模型
    result = TestcaseCreate(
        feature="请手动修改模块名称",  # 需要手动设置模块名称
        title=f"{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())} 导入接口数据",
        url=re.sub(r'\?.*$', '', url.replace(yaml_data["host"], "")),  # 去除 host 部分
        pk=pk,
        method=method.upper(),
        header=json.dumps(str_to_python(headers)),  # 确保 str_to_python 返回字典
        data=json.dumps(str_to_python(data_dict)),  # 确保数据格式正确
        priority="P5",  # 优先级默认 P5
    )

    # 存储测试用例到数据库
    return create_testcase_in_db(result)


@router.post("/send_request_case", summary="执行测试用例", description="发送测试请求并返回结果")
@return_response()
async def send_request_testcases(case: Dict[str, Any]):
    """
    执行测试用例请求，并返回响应信息及断言结果

    :param case: 测试用例的详细信息，包括请求方法、URL、请求头、参数等
    :return: 包含请求参数、响应状态码、响应数据、断言结果的字典
    """
    # 只有当global_key存在且有值时才处理
    if case.get("global_key"):
        global_data = select_globalvariable_in_db(1, 9999, GlobalVariableCreate(global_key=case['global_key']))
        if global_data['data']:
            GLOBAL_VARS.update(json.loads(global_data['data'][0].global_value))

    # 初始化结果字典
    result = {}

    # 发送请求，返回响应和提取参数列表
    response, extracted_list = BaseRequest.send_request(case)

    # 尝试将响应解析为 JSON，否则使用文本格式
    try:
        response_data = response.json()
    except ValueError:
        response_data = response.text

    # 调用断言函数，并确保它返回两个列表
    actual_list, error_list = assert_result(response, case.get("validate", {}))

    # 收集结果
    result["parameter"] = case
    result["code"] = response.status_code
    result["request"] = response_data
    # 使用一个元组保存断言结果，也可以考虑将实际与错误断言结果分别存储
    result["assert"] = (actual_list, error_list)
    result["extracted"] = extracted_list

    return result


@router.post("/get_yaml")
@return_response()
async def get_file_yaml_data():
    yaml = YAML()
    with open(config_path, "r", encoding="utf-8") as f:
        yaml_data = yaml.load(f)

    # 返回的是 CommentedMap，可被转为 dict，但会丢失注释
    # 所以你要保留注释信息，需要保持原样或转字符串返回
    from io import StringIO
    stream = StringIO()
    yaml.dump(yaml_data, stream)
    content_with_comments = stream.getvalue()

    return {"content": content_with_comments}


@router.post("/save_yaml")
@return_response()
async def save_file_yaml_data(yamldata: str = Body(..., embed=True)):
    """
    接收前端传过来的 YAML 字符串（包含注释），保存到 config.yaml 文件中。
    """
    yaml = YAML()
    yaml.indent(mapping=2, sequence=4, offset=2)
    yaml.preserve_quotes = True

    try:
        # 验证 YAML 格式
        stream = StringIO(yamldata)
        data = yaml.load(stream)
    except Exception as e:
        return {"error": f"YAML 解析错误：{str(e)}"}

    # 保存文件
    try:
        with open(config_path, "w", encoding="utf-8") as f:
            yaml.dump(data, f)
    except Exception as e:
        return {"error": f"保存文件失败：{str(e)}"}

    return {"message": "保存成功，配置已更新"}


@router.post("/run_code_python")
def run_code_python(
        payload: CodePayload,
        background_tasks: BackgroundTasks,
):
    if not payload.code:
        raise HTTPException(status_code=400, detail="代码不能为空")

    # 生成临时脚本并注册清理任务
    script_path = generate_script_file(payload.code)
    background_tasks.add_task(cleanup_file, script_path)

    background_tasks.add_task(create_code_script, CodeScriptCreateDTO(
        cod_key=str(uuid.uuid4()),
        name="动态执行脚本",
        code=payload.code
    ))

    # 返回 SSE 流式输出
    return StreamingResponse(
        stream_process(script_path),
        media_type="text/event-stream"
    )


@router.post("/list_code_python")
@return_response()
async def list_code_python(query: CodeScriptQueryDTO):
    return get_code_script_list(query)


@router.post("/save_code_python")
@return_response()
async def save_code_python(dto: CodeScriptCreateDTO):
    dto.code_key = str(uuid.uuid4())
    return create_code_script(dto)
